package cn.bytengine.stem.cluster.netty;

import cn.bytengine.stem.contexts.Context;
import io.netty.bootstrap.AbstractBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.GlobalEventExecutor;

public abstract class NettyServer<B extends AbstractBootstrap<B, C>, C extends Channel> {

    private final EventLoopGroup bossLoopGroup;

    private final EventLoopGroup workerLoopGroup;

    private final ChannelGroup channelGroup;

    public NettyServer(Context config) {
        this.bossLoopGroup = initBossLoopGroup(config);
        this.workerLoopGroup = initWorkerLoopEvent(config);
        this.channelGroup = initChannelGroup(config);
    }

    // region internal methods
    protected EventLoopGroup initBossLoopGroup(Context config) {
        return new NioEventLoopGroup();
    }

    protected EventLoopGroup initWorkerLoopEvent(Context config) {
        return new NioEventLoopGroup();
    }

    protected ChannelGroup initChannelGroup(Context config) {
        return new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    }

    protected EventLoopGroup getBossLoopGroup() {
        return this.bossLoopGroup;
    }

    protected EventLoopGroup getWorkerLoopGroup() {
        return this.workerLoopGroup;
    }

    protected ChannelGroup getChannelGroup() {
        return this.channelGroup;
    }

    protected abstract B bootstrap(Context context, int port, ChannelInitializer<? extends Channel> channelInitializer);

    // endregion

    // region functions
    public abstract void startup(Context context) throws Exception;

    public void shutdown() {
        getChannelGroup().close();
        getBossLoopGroup().shutdownGracefully();
        getWorkerLoopGroup().shutdownGracefully();
    }
    // endregion
}
